package net.towee.client.com.stuff.presenter;

import java.util.Date;

import com.google.common.base.Preconditions;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;

import net.towee.client.com.stuff.GroupInformationShower;
import net.towee.client.com.stuff.presenter.GroupInformationShowerPresenter.Display;
import net.towee.client.event.core.UserRoleChangedEvent;
import net.towee.client.event.core.UserRoleChangedHandler;
import net.towee.client.managers.AccountHelper;
import net.towee.client.managers.GroupsManager;
import net.towee.model.account.AccountGroup;
import net.towee.model.account.AccountPermission;
import net.towee.model.account.Role;
import net.towee.model.group.GroupData;
import net.towee.model.group.GroupOpenness;
import net.towee.model.group.GroupProfile;

public class GroupInformationShowerPresenter implements GroupInformationShower, ClickHandler {

	public interface Display extends IsWidget {
		// Getters
		String getDescription();

		String getHomeUrl();

		String getPrintName();

		String getAlias();

		String getQuestAnswer();
		
		String getQuest();

		GroupOpenness getOpenness();

		// Setters
		void setDescription(String description);

		void setHomeUrl(String homeUrl);

		void setNumberOfUsers(int numberOfUsers);

		void setPrintName(String printName);

		void setAlias(String alias);

		void setOpenness(GroupOpenness openness);

		void setCreationDate(Date creationDate);
		
		void setQuestAnswer(String result);

		// For entering/leaving group
		void showLeaveGroupButton();

		void showEnterToGroupButton();

		void showSendRequestButton();

		void showGroupQuestion(String result);

		// Buttons
		HasClickHandlers getLeaveGropuButton();

		HasClickHandlers getEnterToGroupButton();

		HasClickHandlers getChangeButton();

		// Events
		void canChangeProfile();

		void canChangeQuestion();

		void wrongAnswer();

		void enteringDone();

		void requestingDone();

		void leaveGroupDone();

		void enableEditing();

		void canNotChangeProfile();

		void canNotChangeQuestion();
	}

	// Injected
	private final Display display;
	private final AccountHelper account;
	private final GroupsManager groupsManager;

	private GroupProfile group;
	private GroupData data;
	private boolean saveInModel = false;

	@Inject
	public GroupInformationShowerPresenter(Display display, AccountHelper account,
			GroupsManager groupsManager, HandlerManager eventBus) {
		this.display = display;
		this.account = account;
		this.groupsManager = groupsManager;

		display.getChangeButton().addClickHandler(this);
		display.getEnterToGroupButton().addClickHandler(this);
		display.getLeaveGropuButton().addClickHandler(this);
		
		eventBus.addHandler(UserRoleChangedEvent.TYPE, new UserRoleChangedHandler() {
			@Override
			public void onUserRoleChanged(Role role, String userId, String groupId) {
				if(group.getId().equals( groupId ))
					checkChangePermissions();
			}
		});
	}

	@Override
	public void go(HasWidgets container) {
		Preconditions.checkNotNull(group, "group is null");
		container.clear();
		container.add(display.asWidget());
	}

	@Override
	public void onClick(ClickEvent event) {
		Object source = event.getSource();

		if (source.equals(display.getChangeButton())) {
			saveChangesInModel();

			if (!saveInModel)
				saveChangesOnServer();
		} else if (source.equals(display.getEnterToGroupButton())) {
			enterToGroup();
		} else if (source.equals(display.getLeaveGropuButton())) {
			leaveFromGroup();
		}
	}

	/**
	 * Leave user from group
	 */
	private void leaveFromGroup() {
		groupsManager.leaveGroup(group.getId(), new AsyncCallback<Boolean>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
				display.leaveGroupDone();
			}
		});
	}

	/**
	 * Enter user to group
	 */
	private void enterToGroup() {
		// Get answer
		String answer = display.getQuestAnswer();

		// Send request
		switch (group.getOpenness()) {
		case WITH_REQUEST:
			groupsManager.addEnterRequest(group.getId(), account.getAccount().getId(), answer,
					new AsyncCallback<Boolean>() {
						@Override
						public void onFailure(Throwable caught) {
							display.wrongAnswer();
						}

						@Override
						public void onSuccess(Boolean result) {
							display.requestingDone();
						}
					});
			break;
		case OPEN_GROUP:
			groupsManager.enterToGroup(group.getId(), answer, new AsyncCallback<AccountGroup>() {
				@Override
				public void onFailure(Throwable caught) {
					display.wrongAnswer();
				}

				@Override
				public void onSuccess(AccountGroup result) {
					display.enteringDone();
					checkChangePermissions();
				}
			});
			break;
		}
	}

	/**
	 * Save changes on server side of existing group
	 */
	private void saveChangesOnServer() {
		// Save profile
		if (account.hasPermissions(group.getId(), AccountPermission.CAN_CHANGE_GROUP_PROFILE)) {
			groupsManager.updateProfile(group, new AsyncCallback<Boolean>() {
				@Override
				public void onFailure(Throwable caught) {
				}

				@Override
				public void onSuccess(Boolean result) {
				}
			});

			// Save print name
			groupsManager.updatePrintName(group.getId(), display.getPrintName(),
					new AsyncCallback<Boolean>() {
						@Override
						public void onFailure(Throwable caught) {
						}

						@Override
						public void onSuccess(Boolean result) {
						}
					});
		}

		// Update test question
		if (account.hasPermissions(group.getId(), AccountPermission.CAN_CHANGE_TEST_QUEST))
			groupsManager.updateGroupQuestAnswer(group.getId(), display.getQuest(),
					display.getQuestAnswer(), new AsyncCallback<Boolean>() {
						@Override
						public void onFailure(Throwable caught) {
						}

						@Override
						public void onSuccess(Boolean result) {

						}
					});
	}

	/**
	 * Save changes only in model
	 */
	protected void saveChangesInModel() {
		group.setDescription(display.getDescription());
		group.setHomeUrl(display.getHomeUrl());
		group.setOpenness(display.getOpenness());
		group.getIdentity().setPrintName(display.getPrintName());
		group.getIdentity().setAlias(display.getAlias());
		
		if(data != null) 
			data.setTestQuestion(display.getQuest(), display.getQuestAnswer());
	}
	
	private void checkChangePermissions() {
		// If we can change profile or question
		if (account.hasPermissions(group.getId(), AccountPermission.CAN_CHANGE_TEST_QUEST))
			groupsManager.getGroupQuestionAnswer(group.getId(), new AsyncCallback<String>() {
				@Override
				public void onFailure(Throwable caught) {
				}

				@Override
				public void onSuccess(String result) {
					display.setQuestAnswer(result);
					display.canChangeQuestion();
				}
			});
		else 
			display.canNotChangeQuestion();
			
		
		if (account.hasPermissions(group.getId(), AccountPermission.CAN_CHANGE_GROUP_PROFILE)) 
			display.canChangeProfile();
		else 
			display.canNotChangeProfile();
	}

	@Override
	public void setGroupProfile(GroupProfile id) {
		this.group = id;

		// Set data
		display.setDescription(id.getDescription());
		display.setHomeUrl(id.getHomeUrl());
		display.setNumberOfUsers(id.getNumberOfUsers());
		display.setPrintName(id.getIdentity().getPrintName());
		display.setAlias(id.getIdentity().getAlias());
		display.setOpenness(id.getOpenness());
		display.setCreationDate(id.getCreationDate());

		// Show buttons
		if (!saveInModel) {
			checkChangePermissions();

			// Show button for entering or leaving group
			if (account.isMemeberOfGroups(id.getIdentity().getId()))
				display.showLeaveGroupButton();
			else {
				switch (id.getOpenness()) {
				case OPEN_GROUP:
					display.showEnterToGroupButton();
					break;
				case WITH_REQUEST:
					display.showSendRequestButton();
					break;
				}
			}

			// Get enter request
			if (!id.getOpenness().equals(GroupOpenness.HIDDEN_GROUP)) {
				groupsManager.getGroupQuestion(id.getIdentity().getId(), new AsyncCallback<String>() {
					@Override
					public void onFailure(Throwable caught) {
					}

					@Override
					public void onSuccess(String result) {
						display.showGroupQuestion(result);
					}
				});
			}
		}
	}

	@Override
	public void setNewGroupData(GroupData data) {
		// Set data
		this.data = data;
		this.saveInModel = true;

		// Enable changers
		display.canChangeQuestion();
		display.canChangeProfile();
		display.enableEditing();

		// Set group data
		this.setGroupProfile(data.getProfile());
	}
}